package game.leon.engine.script;

import game.leon.engine.utils.LinkedNode;
import org.apache.log4j.Logger;

import static game.leon.engine.script.Parser.Def.*;
/**
 * User: bianpeng
 * Date: 12-2-3
 */
public class Parser {
    public static interface Def {
        int TOKEN_CHUNK = 1;            // 可执行单位
        int TOKEN_FRAME = 2;      // 括号
        int TOKEN_INT = 3;
        int TOKEN_STR = 4;
        int TOKEN_NUM = 5;
        int TOKEN_SYMBOL = 6;
        
        
        int ERROR_KNOWN = 0;
        int ERROR_END_OF_SOURCE = 1;
    }
    private Logger logger = Logger.getLogger(getClass());
    private Reader reader;
    private int line = 1;
    public Parser(Reader reader) {
        this.reader = reader;
    }
    //
    // parse出一个语法树
    //
    public Token parse() {
        Token root = new Token(TOKEN_CHUNK, line);
        for(;;) {
            try {
                int ch = read();
                if (ch == '(') {
                    advance();
                    root.addChild(parseFrame());
                } else {
                    logger.error("unexpected: " + (char)ch);
                    break;
                }
            } catch (ParseException e) {
                break;
            }
        }
        return root;
    }

    /**
     * 括号
     */
    private Token parseFrame() throws ParseException {
        Token token = new Token(TOKEN_FRAME, line);
        for(;;) {
            int ch = read();
            if (ch == ')') {
                advance();
                break;
            } else {
                Token form = parseForm();
                token.addSibling(form);
            }
        }
        return token;
    }

    private Token parseForm() throws ParseException {
        int ch = read();
        if (ch == '"') {
            advance();
            return parseString();
        } else if (isDigit(ch)) {
            return parseNumber();
        } else if (ch == '(') {
            advance();
            return parseFrame();
        } else if (isSpace(ch)) {
            skipSpaces();
            return parseForm();
        } else if (ch == '\n') {
            advance();
            return parseForm();
        } else {
            return parseSymbol();
        }
    }

    private Token parseSymbol() throws ParseException {
        StringBuilder sb = new StringBuilder();
        for(;;) {
            int ch = read();
            if (isSpace(ch) || ch == '\n' || ch == ')') break;
            sb.append((char)ch);
            advance();
        }
        Token token = new Token(TOKEN_SYMBOL, line);
        token.strVal = sb.toString();
        return token;
    }

    private int parseDecimal() throws ParseException {
        int val = 0;
        for(;;) {
            int ch = read();
            if (!isDigit(ch)) break;
            val = val * 10 + ch - '0';
            advance();
        }
        return val;
    }

    private Token parseNumber() throws ParseException {
        int integer = parseDecimal();
        if (read() != '.') {
            Token token = new Token(TOKEN_INT, line);
            token.intVal = integer;
            return token;
        }
        advance();
        int fraction = parseDecimal();
        Token token = new Token(TOKEN_NUM, line);
        token.numVal = integer + fraction/Math.exp(Math.ceil(Math.log10(fraction))*Math.log(10));
        return token;
    }

    private Token parseString() throws ParseException {
        StringBuilder sb = new StringBuilder();
        for(;;) {
            int ch = read();
            if (ch == '"') { advance();break; }
            sb.append((char)ch);
            advance();
        }
        Token token = new Token(TOKEN_STR, line);
        token.strVal = sb.toString();
        return token;
    }

    private int read() throws ParseException {
        int ch = reader.read();
        if (ch <= 0) {
            throw new ParseException(ERROR_END_OF_SOURCE);
        }
        return ch;
    }
    private void advance() {
        if (reader.read() == '\n') ++line;
        reader.advance();
    }
    private void skipSpaces() {
        while (isSpace(reader.read())) {
            reader.advance();
        }
    }
    public static boolean isSpace(int ch) {
        return ch == ' ' || ch == '\t';
    }
    public static boolean isDigit(int ch) {
        return ch >= '0' && ch <= '9';
    }
    //------------------------------------------------------------------------------------
    // 打印一下
    //------------------------------------------------------------------------------------
    public static void print(Token tree) {
        System.out.printf("line:%d, type:%d, ", tree.getLine(), tree.getType());
        switch (tree.getType()) {
            case TOKEN_INT: System.out.printf("value:%d\n", tree.intVal); break;
            case TOKEN_NUM: System.out.printf("value:%f\n", tree.numVal); break;
            case TOKEN_SYMBOL:
            case TOKEN_STR: System.out.printf("value:\"%s\"\n", tree.strVal); break;
            default: System.out.println(); break;
        }
        LinkedNode<Token> sibling = tree.sibling;
        while (sibling != null) {
            print(sibling.getItem());
            sibling = sibling.getNext();
        }
        LinkedNode<Token> child = tree.child;
        while (child != null) {
            print(child.getItem());
            child = child.getNext();
        }
    }
}
